import struct
import socket
import subprocess

from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QApplication, QGraphicsScene
from WarningDialog import Ui_Form as ErrorDialog
from ManagerLogIn import Ui_Form as ManagerLogInUi
from ManagerReport import Ui_Form as ManagerControlPanelUi


def recv_file(sock: socket.socket):
    fileinfo_size = struct.calcsize('!128sl')
    buf = sock.recv(fileinfo_size)
    if buf:
        filename, filesize = struct.unpack('!128sl', buf)
        fn = filename.strip(b'\00').decode('utf-8')
        print('file new name is {0}, filesize if {1}'.format(str(fn), filesize))
        recvd_size = 0
        file_path = 'recv_' + str(fn)
        fp = open(file_path, 'wb')
        print('start receiving...')
        while not recvd_size == filesize:
            if filesize - recvd_size > 1024:
                data = sock.recv(1024)
                recvd_size += len(data)
            else:
                data = sock.recv(filesize - recvd_size)
                recvd_size = filesize
            fp.write(data)
        fp.close()
        print('end receive...')
        return file_path
    else:
        print('failed to recv file')
        return False


def flush_sock(sock: socket.socket):
    try:
        sock.recv(1024, socket.MSG_DONTWAIT)
    except OSError as e:
        pass


class DialogWidget(QWidget):
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        super().__init__()

    def closeEvent(self, evt) -> None:
        # Todo: Revoke the disabled window element
        self.client.login.logInWindow.setEnabled(True)
        pass


class ClientDialog:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        self.errorDialogWindow = DialogWidget(client)
        self.errorDialogWindowContent = ErrorDialog()
        self.errorDialogWindowContent.setupUi(self.errorDialogWindow)
        self.errorDialogWindowContent.pushButton.clicked.connect(self.errorDialogWindow.close)

    def show_dialog(self, caution_str):
        assert isinstance(caution_str, str)
        self.errorDialogWindowContent.label.setText(
            "<html><head/><body><p align=\"center\"><span style=\" font-weight:600;\">" + caution_str + "</span></p></body></html>")
        self.errorDialogWindow.show()


class ClientControlPanel:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        self.controlPanel = QWidget()
        self.controlPanelContent = ManagerControlPanelUi()
        self.controlPanelContent.setupUi(self.controlPanel)
        self.socketToServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.controlPanelContent.saveReport.clicked.connect(self.save_report)
        self.controlPanelContent.showReport.clicked.connect(self.report_request)
        self.controlPanelContent.formMode.setChecked(True)
        self.img_path = None

    def socket_connect(self):
        try:
            # Todo: Change the address to central server
            self.socketToServer.connect((socket.gethostname(), 8000))
        except socket.error as e:
            print(e)
            return False
        else:
            return True

    def report_request(self):
        flush_sock(self.socketToServer)
        # Todo:
        self.socketToServer.send(struct.pack('!ii', 0,
                                             0 if self.controlPanelContent.formMode.isChecked() else \
                                                 1))
        self.img_path = recv_file(self.socketToServer)
        if self.img_path:
            img = QGraphicsScene()
            img.addPixmap(QPixmap(self.img_path))
            self.controlPanelContent.reporView.setScene(img)
            self.controlPanelContent.reporView.show()
        else:
            self.client.errorDialog.show_dialog("Fail to get report...")

    def save_report(self):
        # Todo:
        save_path = self.controlPanelContent.savePath.text()
        subprocess.run("mv " + self.img_path + " " + save_path, shell=True)

    def show_panel(self):
        self.controlPanel.show()


class ClientLogIn:
    def __init__(self, client):
        assert isinstance(client, Client)
        self.client = client
        self.logInWindow = QWidget()
        self.logInWindowContent = ManagerLogInUi()
        self.logInWindowContent.setupUi(self.logInWindow)

        self.PasswordList = [('Jack', '123456'), ('Bob', '654321')]
        self.logInWindowContent.loginButton.clicked.connect(self.client.login_request)

    def check_passwd(self):
        id_input = self.logInWindowContent.idEdit.text()
        passwd_input = self.logInWindowContent.passwdEdit.text()
        if (id_input, passwd_input) in self.PasswordList:
            return True
        else:
            return False

    def show_login(self):
        self.logInWindow.show()

    def close_login(self):
        self.logInWindowContent.idEdit.setText('')
        self.logInWindowContent.passwdEdit.setText('')
        self.logInWindow.close()


class Client:
    def __init__(self):
        self.login = ClientLogIn(self)
        self.panel = ClientControlPanel(self)
        self.errorDialog = ClientDialog(self)

    def login_request(self):
        if self.login.check_passwd():
            if self.panel.socket_connect():
                flush_sock(self.panel.socketToServer)
                self.panel.socketToServer.send(struct.pack('!iii', 2, 0, 0))
                outcome = self.panel.socketToServer.recv(1024).decode()
                if outcome != 'Connected':
                    self.login.logInWindow.setEnabled(False)
                    self.errorDialog.show_dialog("Fail to connect to central server!")
                else:
                    self.login.close_login()
                    self.panel.show_panel()
            else:
                self.login.logInWindow.setEnabled(False)
                self.errorDialog.show_dialog("Fail to connect to central server!")
        else:
            self.login.logInWindow.setEnabled(False)
            self.errorDialog.show_dialog("ID or Password Wrong!")

    def show(self):
        self.login.show_login()


if __name__ == '__main__':
    clientProcess = QApplication([])
    clientWindow = Client()
    clientWindow.show()
    clientProcess.exec_()
